package controler;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.awt.List;
import java.util.*;

import model.SimulationSettings;

public class MonteCarloThreadable implements Callable<Double>{
	
	private String order;
	private double S;
	private double X;
	private double T;
	private double r;
	private double b;
	private double v;
	private double St;
	private Random randomNo;
	private double Drift;
	private double vSqrdt;
	private int z;
	private int nSteps;
	private int nSimulationsTotal;
	private int nSimulations;
	
	public double finalResult;

	public double test;
	@Override
	public Double call() throws Exception {
		double dt; 
		double St;
		Random randomNo = new Random();

		double Sum = 0; double Drift; double vSqrdt;
		int i; int j; int z = 1; 

		dt = T/nSteps; 
		Drift = (b - v*v/2)*dt ;
		vSqrdt = v * java.lang.Math.sqrt(dt) ;
		if (order=="Call"){ 
			z = 1 ;
		}
		else if (order=="Put"){ 
			z = -1 ;
		}
		for (i = 1; i<=nSimulations; i++)
		{
			St = S ;
			for (j = 1; j<=nSteps; j++)
			{
				double randNumber = (double) randomNo.nextGaussian();
				St = St * java.lang.Math.exp(Drift + vSqrdt * randNumber); 
			}
			Sum = Sum +  java.lang.Math.max(z*(St - X), 0) ;
		}
		return (java.lang.Math.exp(-r*T)*(Sum/nSimulations)) ;
	}

	
	MonteCarloThreadable(String order, double S, double X, double T, double r, double b, 
			double v, int nSteps, int nSimulations)
	{
		this.order = order;
		this.S = S;
		this.X = X;
		this.T = T;
		this.r = r;
		this.b = b;
		this.v = v;
		this.nSteps = nSteps;
		this.nSimulationsTotal = nSimulations/10;
		this.nSimulations = 10;
	}
	
	
	
	public void myCall()
	{
		int cores = Runtime.getRuntime().availableProcessors();
		// Create a pool thread
		ExecutorService exec = Executors.newFixedThreadPool(cores);
		
		ArrayList<Future<Double>> listPreResults = new ArrayList<Future<Double>>();
		
		
		double resultat = 0;

		double Sum = 0;
		for (int i = 0; i< nSimulationsTotal; i++)
		{
			Future<Double> future = exec.submit(new MonteCarloThreadable(order, S, X, T, r, b, 
					v, nSteps, nSimulations));
			listPreResults.add(future);
		}
		for(Future<Double> fut : listPreResults){
            try {
            	//Sum = Sum +  java.lang.Math.max(z*(fut.get() - X), 0) ;
            	test = test + fut.get();
                //System.out.println("resultat : "+fut.get());

                
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
		
       	resultat = test/nSimulationsTotal;
       	System.out.println("valeur finale de Sum: "+ test);
       	System.out.println("number of simulations: "+ nSimulationsTotal);
       	System.out.println("resultat final : "+resultat);
        
        //shut down the executor service now
        exec.shutdown();
		
	}
	
	public void setOptions(SimulationSettings settings) {
		
		this.order = settings.orderType;
		this.S = settings.currentPrice;
		this.X = settings.strikePrice;
		this.T = settings.timeMaturity;
		this.r = settings.interest;
		this.b = settings.fees;
		this.v = settings.volatility;
		this.nSteps = (int) settings.steps;
		this.nSimulationsTotal = (int) settings.simulation;
		
		
	}
	

}
